from __future__ import annotations

from PySide2.QtCore import QObject, Slot, Signal, QThread, QMutex, QWaitCondition, QMutexLocker

from .CommonUtil import curTimeMills

class Message:
    action: int
    arg: any = None
    onBackground: callable
    onFinish: Signal
    timestamp: int
    next: Message

class MessageQueue():

    def __init__(self):
        self._messageHead: Message = None
        self._syncMutex: QMutex = QMutex()
        self._pollMutex: QMutex = QMutex()
        self._pollCon: QWaitCondition = QWaitCondition()
        self._blocked: bool = False
        self._quitting: bool = False

    def enqueue(self, msg:Message):
        with QMutexLocker(self._syncMutex):
            if self._quitting:
                return
            p: Message = self._messageHead
            needWake: bool = False
            print(msg)
            if p is None or msg.timestamp < p.timestamp:
                print("enqueue put HEAD")
                self._messageHead = msg
                msg.next = p
                needWake = self._blocked
            else:
                print("enqueue put MIDDLE")
                prev: Message
                while True:
                    prev = p
                    p = p.next
                    if (not p) or (msg.timestamp < p.timestamp):
                        break
                msg.next = p
                prev.next = msg
            print("enqueue, needWake = " + str(needWake))
            if needWake:
                print("enqueue, before wakeAll")
                self._pollMutex.lock()
                self._pollCon.wakeAll()
                self._pollMutex.unlock()
                print("enqueue, after wakeAll")


    def dequeue(self) -> Message:
        blockTimeoutMillis: int = 0
        while True:
            print('dequeue, while in')
            if blockTimeoutMillis == -1:
                print('dequeue, before wait')
                self._pollMutex.lock()
                self._pollCon.wait(self._pollMutex)
                self._pollMutex.unlock()
                print('dequeue, after wait')
            elif blockTimeoutMillis > 0:
                print('dequeue, before wait: ' + str(blockTimeoutMillis))
                self._pollMutex.lock()
                self._pollCon.wait(self._pollMutex, blockTimeoutMillis)
                self._pollMutex.unlock()
                print('dequeue, after wait')
            with QMutexLocker(self._syncMutex):
                now = curTimeMills()
                msg = self._messageHead
                print(msg)
                if isinstance(msg, Message):
                    print("MessageQueue has Message")
                    if now < msg.timestamp:
                        blockTimeoutMillis = msg.timestamp - now
                    else:
                        # Got a message
                        self._blocked = False
                        self._messageHead = msg.next
                        msg.next = None
                        return msg
                else:
                    # No more messages
                    blockTimeoutMillis = -1
                if self._quitting:
                    return None
                self._blocked = True

    def removeAllMessages(self):
        self._messageHead = None


    def quit(self):
        if self._quitting:
            return
        self._quitting = True
        self._pollMutex.lock()
        QWaitCondition.wakeAll()
        self._pollMutex.unlock()


    def isEmpty(self) -> bool:
        return self._messageHead is None

class Handler(QObject):
    handler = Signal(Message)

class Worker(QThread):
    def __init__(self):
        super().__init__()
        self._messageQueue = MessageQueue()

    def run(self):
        while not self.isFinished():
            msg: Message = self._messageQueue.dequeue()
            print("got message")
            if msg is None:
                break
            else:
                print(msg)
                print (type(msg.onBackground))
                if not msg.onBackground is None:
                    print("run before onBackground")
                    if msg.arg is None:
                        result = msg.onBackground()
                    else:
                        result = msg.onBackground(msg.arg)
                    print("run after onBackground")
                if isinstance(msg.onFinish, Signal):
                    print("run before onFinish")
                    msg.onFinish.emit(result)
                    print("run after onFinish")

    def post(self, msg:Message):
        msg.timestamp = curTimeMills()
        self._messageQueue.enqueue(msg)

    def postDelay(self, msg:Message, delay: int):
        msg.timestamp = curTimeMills() + delay
        self._messageQueue.enqueue(msg)

    def postAtTime(self, msg: Message, timestamp: int):
        msg.timestamp = timestamp
        self._messageQueue.enqueue(msg)

    def quit(self):
        self._messageQueue.quit()
        super().quit()

